/*
 * Copyright 2002-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context.annotation;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.UnknownHostException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringJoiner;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
import org.springframework.context.annotation.DeferredImportSelector.Group;
import org.springframework.core.NestedIOException;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

/**
 * Parses a {@link Configuration} class definition, populating a collection of
 * {@link ConfigurationClass} objects (parsing a single Configuration class may result in
 * any number of ConfigurationClass objects because one Configuration class may import
 * another using the {@link Import} annotation).
 *
 * <p>This class helps separate the concern of parsing the structure of a Configuration
 * class from the concern of registering BeanDefinition objects based on the content of
 * that model (with the exception of {@code @ComponentScan} annotations which need to be
 * registered immediately).
 *
 * <p>This ASM-based implementation avoids reflection and eager class loading in order to
 * interoperate effectively with lazy class loading in a Spring ApplicationContext.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @author Phillip Webb
 * @author Sam Brannen
 * @author Stephane Nicoll
 * @since 3.0
 * @see ConfigurationClassBeanDefinitionReader
 */
class ConfigurationClassParser {

	private static final PropertySourceFactory DEFAULT_PROPERTY_SOURCE_FACTORY = new DefaultPropertySourceFactory();

	private static final Comparator<DeferredImportSelectorHolder> DEFERRED_IMPORT_COMPARATOR =
			(o1, o2) -> AnnotationAwareOrderComparator.INSTANCE.compare(o1.getImportSelector(), o2.getImportSelector());


	private final Log logger = LogFactory.getLog(getClass());

	private final MetadataReaderFactory metadataReaderFactory;

	private final ProblemReporter problemReporter;

	private final Environment environment;

	private final ResourceLoader resourceLoader;

	private final BeanDefinitionRegistry registry;

	private final ComponentScanAnnotationParser componentScanParser;

	private final ConditionEvaluator conditionEvaluator;

	private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>();

	private final Map<String, ConfigurationClass> knownSuperclasses = new HashMap<>();

	private final List<String> propertySourceNames = new ArrayList<>();

	private final ImportStack importStack = new ImportStack();

	private final DeferredImportSelectorHandler deferredImportSelectorHandler = new DeferredImportSelectorHandler();

	private final SourceClass objectSourceClass = new SourceClass(Object.class);


	/**
	 * Create a new {@link ConfigurationClassParser} instance that will be used
	 * to populate the set of configuration classes.
	 */
	public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
			ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
			BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {

		this.metadataReaderFactory = metadataReaderFactory;
		this.problemReporter = problemReporter;
		this.environment = environment;
		this.resourceLoader = resourceLoader;
		this.registry = registry;
		this.componentScanParser = new ComponentScanAnnotationParser(
				environment, resourceLoader, componentScanBeanNameGenerator, registry);
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
	}

	/**
	 * 解析配置类候选集（通常是带有 @Configuration 注解的类），处理其中的 Spring 配置元数据。
	 * 包括：@Bean 方法、@ComponentScan、@Import、@PropertySource 等注解的解析。
	 *
	 * @param configCandidates 配置类候选集合（BeanDefinitionHolder 对象集合）
	 */
	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		// 1. 遍历所有配置类候选
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				// 2. 根据 BeanDefinition 类型选择不同的解析策略
				if (bd instanceof AnnotatedBeanDefinition) {
					// 2.1 如果是 AnnotatedBeanDefinition（通常来自注解配置），直接使用其预解析的元数据
 					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					// 2.2 如果是 AbstractBeanDefinition 且类已加载，使用反射获取类元数据
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				}
				else {
					// 2.3 其他情况（如未加载的类），通过类名进行解析（使用 ASM 字节码分析）
					parse(bd.getBeanClassName(), holder.getBeanName());
				}
			}
			catch (BeanDefinitionStoreException ex) {
				// 3. 直接抛出已有的 BeanDefinitionStoreException
				throw ex;
			}
			catch (Throwable ex) {
				// 4. 将其他异常包装为 BeanDefinitionStoreException
				throw new BeanDefinitionStoreException(
						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
			}
		}

		// 5. 处理所有延迟导入选择器（DeferredImportSelector）
		// springBoot自动装配的延迟加载也是这里调用
		// 这是为了确保 @Conditional 条件判断能在所有常规配置类处理完成后执行
		this.deferredImportSelectorHandler.process();
	}

	protected final void parse(@Nullable String className, String beanName) throws IOException {
		Assert.notNull(className, "No bean class name for configuration class bean definition");
		MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
		processConfigurationClass(new ConfigurationClass(reader, beanName));
	}

	protected final void parse(Class<?> clazz, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(clazz, beanName));
	}

	protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(metadata, beanName));
	}

	/**
	 * Validate each {@link ConfigurationClass} object.
	 * @see ConfigurationClass#validate
	 */
	public void validate() {
		for (ConfigurationClass configClass : this.configurationClasses.keySet()) {
			configClass.validate(this.problemReporter);
		}
	}

	public Set<ConfigurationClass> getConfigurationClasses() {
		return this.configurationClasses.keySet();
	}


	/**
	 * 处理单个配置类，包括条件跳过检查、重复类处理、递归处理父类层次结构等。
	 * 这是配置类解析的核心方法之一。
	 *
	 * @param configClass 要处理的配置类
	 * @throws IOException 如果读取类元数据时发生I/O错误
	 */
	protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
		// 1. 条件检查：根据@Conditional注解决定是否跳过当前配置类
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			// 如果shouldSkip返回true，表示当前配置类应被跳过（不处理）
			return;
		}

		// 2. 处理重复的配置类（可能通过不同方式导入）
		ConfigurationClass existingClass = this.configurationClasses.get(configClass);
		if (existingClass != null) {
			// 2.1 如果当前配置类是通过@Import导入的
			if (configClass.isImported()) {
				if (existingClass.isImported()) {
					// 合并导入来源信息（保留导入链关系）
					existingClass.mergeImportedBy(configClass);
				}
				// 如果已存在的类不是通过@Import导入的，则保留已存在的类（优先使用显式定义的类）
				return;
			}
			else {
				// 2.2 当前配置类是显式定义的（非导入），替换已存在的配置类
				this.configurationClasses.remove(configClass);
				// 同时从已知父类映射中移除（如果有）
				this.knownSuperclasses.values().removeIf(configClass::equals);
			}
		}

		// 3. 递归处理配置类及其父类层次结构
		// 将配置类包装为SourceClass（统一处理各种来源的类）
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			// 3.1 核心处理逻辑（解析注解、成员类等）
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);  // 循环处理直到没有父类需要处理

		// 4. 将处理完成的配置类存入缓存
		this.configurationClasses.put(configClass, configClass);
	}
	/**
	 * 核心配置类处理方法：读取注解、成员和方法，构建完整的 ConfigurationClass 元数据。
	 * 该方法采用分层处理策略，按特定顺序处理不同类型的配置元素。
	 *
	 * @param configClass 正在构建的配置类对象（存储解析结果）
	 * @param sourceClass 当前处理的源类（提供元数据访问）
	 * @return 如果存在未处理的父类则返回父类SourceClass，否则返回null
	 * @throws IOException 当读取类元数据时发生I/O错误
	 */
	@Nullable
	protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass) throws IOException {

		// ========== 1. 处理嵌套类（成员类） ==========
		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// 优先递归处理嵌套类（保证内部类优先于外部类处理）
			processMemberClasses(configClass, sourceClass);
		}

		// ========== 2. 处理@PropertySource注解 ==========
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class, PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				// 加载指定的属性文件到环境变量
				processPropertySource(propertySource);
			} else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// ========== 3. 处理@ComponentScan注解 ==========
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// 立即执行组件扫描（扫描指定包路径下的@Component类）
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

				// 检查扫描到的Bean定义中是否包含新的配置类
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					// 如果扫描到的类是配置类，递归解析
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// ========== 4. 处理@Import注解 ==========，springBoot的注解，自动装配import就是这里实现的
		//导入额外的配置类，并完成实例化工作
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		// ========== 5. 处理@ImportResource注解 ==========
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
			if (importResource != null) {
			// 解析XML资源路径（支持占位符）
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// ========== 6. 处理@Bean方法 ==========
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			// 将每个@Bean方法注册为BeanMethod定义
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// ========== 7. 处理接口默认方法 ==========
		processInterfaces(configClass, sourceClass);

		// ========== 8. 处理父类 ==========
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			// 过滤java.*开头的类和已处理的父类
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// 返回父类以便继续处理
				return sourceClass.getSuperClass();
			}
		}

		// 没有父类需要处理，返回null结束递归
		return null;
	}

	/**
	 * Register member (nested) classes that happen to be configuration classes themselves.
	 */
	private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
		Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
		if (!memberClasses.isEmpty()) {
			List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
			for (SourceClass memberClass : memberClasses) {
				if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
						!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
					candidates.add(memberClass);
				}
			}
			OrderComparator.sort(candidates);
			for (SourceClass candidate : candidates) {
				if (this.importStack.contains(configClass)) {
					this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
				}
				else {
					this.importStack.push(configClass);
					try {
						processConfigurationClass(candidate.asConfigClass(configClass));
					}
					finally {
						this.importStack.pop();
					}
				}
			}
		}
	}

	/**
	 * Register default methods on interfaces implemented by the configuration class.
	 */
	private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
		for (SourceClass ifc : sourceClass.getInterfaces()) {
			Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
			for (MethodMetadata methodMetadata : beanMethods) {
				if (!methodMetadata.isAbstract()) {
					// A default method or other concrete method on a Java 8+ interface...
					configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
				}
			}
			processInterfaces(configClass, ifc);
		}
	}

	/**
	 * 检索给定源类中所有带有@Bean注解的方法的元数据。
	 *
	 * 该方法首先通过标准反射获取@Bean方法，但当发现多个@Bean方法时，
	 * 会尝试使用ASM字节码读取工具重新获取方法以确保声明顺序的一致性。
	 * 这是因为JVM的标准反射API返回的方法顺序是不确定的，甚至同一应用在同一JVM的不同运行中也可能不同。
	 *
	 * @param sourceClass 要分析的源类(SourceClass包装类)
	 * @return 包含所有@Bean方法元数据的集合，会尽量保持方法在源代码中的声明顺序
	 */
	private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
		// 获取原始类的注解元数据
		AnnotationMetadata original = sourceClass.getMetadata();

		// 通过反射获取所有带有@Bean注解的方法元数据
		Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());

		// 如果发现多个@Bean方法且元数据是通过标准反射获取的
		if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
			// 尝试通过ASM读取类文件以获得确定性的方法声明顺序
			try {
				// 使用MetadataReaderFactory通过ASM读取类文件
				AnnotationMetadata asm =
						this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();

				// 通过ASM获取所有带有@Bean注解的方法元数据
				Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());

				// 确保ASM找到的方法数量不少于反射找到的数量
				if (asmMethods.size() >= beanMethods.size()) {
					// 创建用于保存匹配方法的集合(使用LinkedHashSet保持顺序)
					Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());

					// 遍历ASM找到的所有方法
					for (MethodMetadata asmMethod : asmMethods) {
						// 在反射找到的方法中查找同名方法
						for (MethodMetadata beanMethod : beanMethods) {
							if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
								// 找到匹配的方法，添加到结果集
								selectedMethods.add(beanMethod);
								break;
							}
						}
					}

					// 如果所有反射方法都在ASM方法集中找到了匹配项
					if (selectedMethods.size() == beanMethods.size()) {
						// 使用ASM顺序的方法集合
						beanMethods = selectedMethods;
					}
				}
			}
			catch (IOException ex) {
				// ASM读取失败时记录调试日志，但继续使用原始反射获取的元数据
				logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);
			}
		}

		// 返回最终的@Bean方法元数据集
		return beanMethods;
	}

	/**
	 * 处理给定的@PropertySource注解元数据，加载并注册属性源。
	 *
	 * 该方法负责解析@PropertySource注解的各个属性，包括：
	 * - 属性源名称
	 * - 字符编码
	 * - 属性文件位置
	 * - 是否忽略资源未找到错误
	 * - 自定义属性源工厂
	 *
	 * 处理流程：
	 * 1. 解析注解属性值
	 * 2. 处理属性文件位置中的占位符
	 * 3. 加载属性资源
	 * 4. 创建并注册属性源
	 *
	 * @param propertySource @PropertySource注解的属性元数据
	 * @throws IOException 如果加载属性源失败且未设置ignoreResourceNotFound
	 * @throws IllegalArgumentException 如果必要参数无效
	 */
	private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
		// 1. 处理属性源名称配置
		String name = propertySource.getString("name");
		if (!StringUtils.hasLength(name)) {
			name = null; // 使用默认名称
		}

		// 2. 处理字符编码配置
		String encoding = propertySource.getString("encoding");
		if (!StringUtils.hasLength(encoding)) {
			encoding = null; // 使用默认编码
		}

		// 3. 获取属性文件位置数组
		String[] locations = propertySource.getStringArray("value");
		// 验证至少指定了一个位置
		Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");

		// 4. 获取是否忽略资源未找到错误的配置
		boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");

		// 5. 处理自定义属性源工厂
		Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
		PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
				DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

		// 6. 遍历所有指定的位置
		for (String location : locations) {
			try {
				// 6.1 解析位置字符串中的占位符
				String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
				// 6.2 获取资源对象
				Resource resource = this.resourceLoader.getResource(resolvedLocation);
				// 6.3 创建并添加属性源
				addPropertySource(factory.createPropertySource(
						name, // 属性源名称
						new EncodedResource(resource, encoding) // 编码后的资源
				));
			}
			catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) {
				// 处理占位符解析失败或资源未找到的情况
				if (ignoreResourceNotFound) {
					// 记录忽略的资源未找到信息
					if (logger.isInfoEnabled()) {
						logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
					}
				}
				else {
					// 抛出原始异常
					throw ex;
				}
			}
		}
	}
	private void addPropertySource(PropertySource<?> propertySource) {
		String name = propertySource.getName();
		MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();

		if (this.propertySourceNames.contains(name)) {
			// We've already added a version, we need to extend it
			PropertySource<?> existing = propertySources.get(name);
			if (existing != null) {
				PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
						((ResourcePropertySource) propertySource).withResourceName() : propertySource);
				if (existing instanceof CompositePropertySource) {
					((CompositePropertySource) existing).addFirstPropertySource(newSource);
				}
				else {
					if (existing instanceof ResourcePropertySource) {
						existing = ((ResourcePropertySource) existing).withResourceName();
					}
					CompositePropertySource composite = new CompositePropertySource(name);
					composite.addPropertySource(newSource);
					composite.addPropertySource(existing);
					propertySources.replace(name, composite);
				}
				return;
			}
		}

		if (this.propertySourceNames.isEmpty()) {
			propertySources.addLast(propertySource);
		}
		else {
			String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
			propertySources.addBefore(firstProcessed, propertySource);
		}
		this.propertySourceNames.add(name);
	}


	/**
	 * Returns {@code @Import} class, considering all meta-annotations.
	 */
	private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
		Set<SourceClass> imports = new LinkedHashSet<>();
		Set<SourceClass> visited = new LinkedHashSet<>();
		collectImports(sourceClass, imports, visited);
		return imports;
	}

	/**
	 * 递归收集所有声明的 {@code @Import} 注解值。与大多数元注解不同，
	 * 允许在同一个类上声明多个不同值的 {@code @Import} 注解（例如通过直接声明和元注解组合），
	 * 因此不能仅返回类上第一个遇到的注解值。
	 *
	 * <p>典型场景：{@code @Configuration} 类可能同时包含：
	 * 1. 直接声明的 {@code @Import} 注解
	 * 2. 通过 {@code @EnableXXX} 等复合注解引入的元注解 {@code @Import}
	 *
	 * 实现策略：
	 * 1. 深度优先递归扫描所有注解层级
	 * 2. 使用 visited 集合防止循环引用导致的无限递归
	 * 3. 优先处理元注解链，最后处理当前类的 {@code @Import}
	 *
	 * @param sourceClass 要扫描的源类（当前递归层级的目标类/注解）
	 * @param imports     累积导入结果的集合（存储所有发现的 {@code @Import} 值）
	 * @param visited     已访问类的记录集，用于避免重复处理和循环递归
	 * @throws IOException 当读取类元数据失败时抛出
	 */
	private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
			throws IOException {

		// 检查当前类是否已处理过：首次访问返回 true 并执行后续逻辑
		if (visited.add(sourceClass)) {
			// 阶段1：递归处理当前类的所有注解（深度优先）
			for (SourceClass annotation : sourceClass.getAnnotations()) {
				String annName = annotation.getMetadata().getClassName();

				// 关键逻辑：跳过 @Import 注解本身（防止自递归）
				// 原因：@Import 注解通常不携带其他元注解，且其 value 将在阶段2单独处理
				if (!annName.equals(Import.class.getName())) {
					// 递归扫描非 @Import 注解（可能包含元注解层级的 @Import）
					collectImports(annotation, imports, visited);
				}
			}

			// 阶段2：处理当前类上的直接 @Import 注解
			// 注意：此处处理包括：
			//   a) 类上直接声明的 @Import
			//   b) 经过阶段1递归后，当前类作为元注解容器携带的 @Import
			imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
		}
		// 若当前类已访问过，直接跳过（递归终止条件）
	}
	/**
	 * 处理配置类的@Import注解导入的类。
	 *
	 * 该方法会根据导入的候选类类型(
	 * 普通配置类、ImportSelector或ImportBeanDefinitionRegistrar)进行不同的处理，
	 * 并处理可能的循环导入问题。
	 *
	 * @param configClass 当前正在处理的配置类
	 * @param currentSourceClass 当前源类(可能是配置类本身或其内部类)
	 * @param importCandidates 通过@Import注解导入的候选类集合
	 * @param checkForCircularImports 是否检查循环导入的标志
	 */
	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
								Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

		// 如果没有导入候选类，直接返回
		if (importCandidates.isEmpty()) {
			return;
		}

		// 如果需要检查循环导入且当前配置类已经在导入栈中(表示循环导入)
		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
			// 报告循环导入错误
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		}
		else {
			// 将当前配置类压入导入栈(用于检测循环导入)
			this.importStack.push(configClass);
			try {
				// 遍历所有导入候选类
				for (SourceClass candidate : importCandidates) {
					// 检查候选类是否是ImportSelector类型
					if (candidate.isAssignable(ImportSelector.class)) {
						// 候选类是ImportSelector -> 委托它来决定需要导入的类
						Class<?> candidateClass = candidate.loadClass();
						// 实例化ImportSelector
						ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
								this.environment, this.resourceLoader, this.registry);

						// 处理延迟导入选择器(DeferredImportSelector)
						if (selector instanceof DeferredImportSelector) {
							// 交给专门的处理器处理延迟导入
							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
						}
						else {
							// 普通ImportSelector:立即选择需要导入的类
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							// 将类名转换为SourceClass集合
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
							// 递归处理这些导入的类(不检查循环导入，因为ImportSelector应该自己保证)
							processImports(configClass, currentSourceClass, importSourceClasses, false);
						}
					}
					// 检查候选类是否是ImportBeanDefinitionRegistrar类型
					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
						// 候选类是ImportBeanDefinitionRegistrar ->
						// 委托它来注册额外的bean定义
						Class<?> candidateClass = candidate.loadClass();
						// 实例化ImportBeanDefinitionRegistrar
						ImportBeanDefinitionRegistrar registrar =
								ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
										this.environment, this.resourceLoader, this.registry);
						// 将注册器添加到配置类中，稍后处理
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					}
					else {
						// 候选类既不是ImportSelector也不是ImportBeanDefinitionRegistrar ->
						// 将其视为普通@Configuration类处理

						// 在导入栈中注册这个导入关系
						this.importStack.registerImport(
								currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						// 将候选类作为配置类处理
						processConfigurationClass(candidate.asConfigClass(configClass));
					}
				}
			}
			// 异常处理块
			catch (BeanDefinitionStoreException ex) {
				// 如果是BeanDefinitionStoreException，直接抛出
				throw ex;
			}
			catch (Throwable ex) {
				// 其他异常包装为BeanDefinitionStoreException抛出
				throw new BeanDefinitionStoreException(
						"Failed to process import candidates for configuration class [" +
								configClass.getMetadata().getClassName() + "]", ex);
			}
			finally {
				// 无论成功与否，都将当前配置类从导入栈弹出
				this.importStack.pop();
			}
		}
	}

	private boolean isChainedImportOnStack(ConfigurationClass configClass) {
		if (this.importStack.contains(configClass)) {
			String configClassName = configClass.getMetadata().getClassName();
			AnnotationMetadata importingClass = this.importStack.getImportingClassFor(configClassName);
			while (importingClass != null) {
				if (configClassName.equals(importingClass.getClassName())) {
					return true;
				}
				importingClass = this.importStack.getImportingClassFor(importingClass.getClassName());
			}
		}
		return false;
	}

	ImportRegistry getImportRegistry() {
		return this.importStack;
	}


	/**
	 * 工厂方法：将 {@link ConfigurationClass} 转换为统一的 {@link SourceClass} 表示形式。
	 * 该方法用于统一处理不同来源的配置类（已加载的Class对象 vs 未加载的类名）。
	 *
	 * @param configurationClass 需要转换的配置类对象
	 * @return 包装后的 SourceClass 对象
	 * @throws IOException 当通过类名获取元数据时发生I/O错误（如类文件读取失败）
	 */
	private SourceClass asSourceClass(ConfigurationClass configurationClass) throws IOException {
		// 1. 获取配置类的注解元数据
		AnnotationMetadata metadata = configurationClass.getMetadata();

		// 2. 检查是否是标准注解元数据（基于已加载的Class对象）
		if (metadata instanceof StandardAnnotationMetadata) {
			// 2.1 如果元数据来自已加载的类，直接使用Class对象创建SourceClass
			// 这样可以避免重复解析注解信息，提高效率
			return asSourceClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass());
		}

		// 3. 默认情况：通过类名创建SourceClass（适用于基于ASM的元数据）
		// 这种情况适用于：
		// - 类尚未被JVM加载
		// - 使用字节码分析获取的元数据
		return asSourceClass(metadata.getClassName());
	}
	/**
	 * Factory method to obtain a {@link SourceClass} from a {@link Class}.
	 */
	SourceClass asSourceClass(@Nullable Class<?> classType) throws IOException {
		if (classType == null || classType.getName().startsWith("java.lang.annotation.")) {
			return this.objectSourceClass;
		}
		try {
			// Sanity test that we can reflectively read annotations,
			// including Class attributes; if not -> fall back to ASM
			for (Annotation ann : classType.getDeclaredAnnotations()) {
				AnnotationUtils.validateAnnotation(ann);
			}
			return new SourceClass(classType);
		}
		catch (Throwable ex) {
			// Enforce ASM via class name resolution
			return asSourceClass(classType.getName());
		}
	}

	/**
	 * Factory method to obtain {@link SourceClass SourceClasss} from class names.
	 */
	private Collection<SourceClass> asSourceClasses(String... classNames) throws IOException {
		List<SourceClass> annotatedClasses = new ArrayList<>(classNames.length);
		for (String className : classNames) {
			annotatedClasses.add(asSourceClass(className));
		}
		return annotatedClasses;
	}

	/**
	 * Factory method to obtain a {@link SourceClass} from a class name.
	 */
	SourceClass asSourceClass(@Nullable String className) throws IOException {
		if (className == null || className.startsWith("java.lang.annotation.")) {
			return this.objectSourceClass;
		}
		if (className.startsWith("java")) {
			// Never use ASM for core java types
			try {
				return new SourceClass(ClassUtils.forName(className,
						this.resourceLoader.getClassLoader()));
			}
			catch (ClassNotFoundException ex) {
				throw new NestedIOException(
						"Failed to load class [" + className + "]", ex);
			}
		}
		return new SourceClass(
				this.metadataReaderFactory.getMetadataReader(className));
	}


	@SuppressWarnings("serial")
	private static class ImportStack extends ArrayDeque<ConfigurationClass> implements ImportRegistry {

		private final MultiValueMap<String, AnnotationMetadata> imports = new LinkedMultiValueMap<>();

		public void registerImport(AnnotationMetadata importingClass, String importedClass) {
			this.imports.add(importedClass, importingClass);
		}

		@Override
		@Nullable
		public AnnotationMetadata getImportingClassFor(String importedClass) {
			return CollectionUtils.lastElement(this.imports.get(importedClass));
		}

		@Override
		public void removeImportingClass(String importingClass) {
			for (List<AnnotationMetadata> list : this.imports.values()) {
				for (Iterator<AnnotationMetadata> iterator = list.iterator(); iterator.hasNext();) {
					if (iterator.next().getClassName().equals(importingClass)) {
						iterator.remove();
						break;
					}
				}
			}
		}

		/**
		 * Given a stack containing (in order)
		 * <ul>
		 * <li>com.acme.Foo</li>
		 * <li>com.acme.Bar</li>
		 * <li>com.acme.Baz</li>
		 * </ul>
		 * return "[Foo->Bar->Baz]".
		 */
		@Override
		public String toString() {
			StringJoiner joiner = new StringJoiner("->", "[", "]");
			for (ConfigurationClass configurationClass : this) {
				joiner.add(configurationClass.getSimpleName());
			}
			return joiner.toString();
		}
	}


	private class DeferredImportSelectorHandler {

		@Nullable
		private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();

		/**
		 * Handle the specified {@link DeferredImportSelector}. If deferred import
		 * selectors are being collected, this registers this instance to the list. If
		 * they are being processed, the {@link DeferredImportSelector} is also processed
		 * immediately according to its {@link DeferredImportSelector.Group}.
		 * @param configClass the source configuration class
		 * @param importSelector the selector to handle
		 */
		public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
			DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(
					configClass, importSelector);
			if (this.deferredImportSelectors == null) {
				DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
				handler.register(holder);
				handler.processGroupImports();
			}
			else {
				this.deferredImportSelectors.add(holder);
			}
		}

		public void process() {
			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
			this.deferredImportSelectors = null;
			try {
				if (deferredImports != null) {
					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
					deferredImports.forEach(handler::register);
					handler.processGroupImports();
				}
			}
			finally {
				this.deferredImportSelectors = new ArrayList<>();
			}
		}

	}


	private class DeferredImportSelectorGroupingHandler {

		private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();

		private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();

		public void register(DeferredImportSelectorHolder deferredImport) {
			Class<? extends Group> group = deferredImport.getImportSelector()
					.getImportGroup();
			DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
					(group != null ? group : deferredImport),
					key -> new DeferredImportSelectorGrouping(createGroup(group)));
			grouping.add(deferredImport);
			this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
					deferredImport.getConfigurationClass());
		}

		/**
		 * 处理所有延迟导入选择器(DeferredImportSelector)的分组导入配置。
		 *
		 * <p>此方法在配置类解析的后期阶段执行，专门处理通过 {@code @Import} 引入的 {@link DeferredImportSelector}
		 * 实现类选择的配置类。这些选择器被分组处理以提高效率，并支持条件配置（如 {@code @Conditional} 注解）。
		 *
		 * <p>执行流程：
		 * 1. 遍历所有分组（按选择器类型分组）
		 * 2. 对每个分组获取其选择的导入类
		 * 3. 将每个导入类作为配置类处理（递归解析其配置）
		 *
		 * <p>设计要点：
		 * - 延迟处理机制：确保条件配置在常规配置解析完成后执行
		 * - 异常包装：将底层异常转换为 Spring 标准存储异常
		 * - 递归处理：通过 processImports() 实现配置类的深度解析
		 *
		 * <p>典型场景：处理 Spring Boot 的 {@code @EnableAutoConfiguration} 自动配置导入
		 *
		 * @see DeferredImportSelector
		 * @see ConfigurationClassParser#processImports
		 */
		public void processGroupImports() {
			// 遍历所有分组（按 DeferredImportSelector 类型分组）
			for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {

				// 获取当前分组选择的所有导入类（通过 selectImports() 方法选择）
				grouping.getImports().forEach(entry -> {

					// 从缓存中获取对应的配置类元数据
					ConfigurationClass configurationClass = this.configurationClasses.get(
							entry.getMetadata());

					try {
						// 核心处理：递归处理导入类（可能包含嵌套配置）
						processImports(
								configurationClass, // 当前配置类（父级）
								asSourceClass(configurationClass), // 转换为统一元数据表示
								asSourceClasses(entry.getImportClassName()), // 导入的候选类集合
								false // 非延迟导入标志（此处处理的是延迟导入的结果）
						);
					}
					// 异常处理链：保留原始存储异常
					catch (BeanDefinitionStoreException ex) {
						throw ex; // 直接抛出已知存储异常
					}
					// 捕获其他所有异常并转换为标准存储异常
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to process import candidates for configuration class [" +
										configurationClass.getMetadata().getClassName() + "]",
								ex // 保留原始异常信息
						);
					}
				});
			}
		}

		private Group createGroup(@Nullable Class<? extends Group> type) {
			Class<? extends Group> effectiveType = (type != null ? type
					: DefaultDeferredImportSelectorGroup.class);
			Group group = ParserStrategyUtils.instantiateClass(effectiveType, Group.class,
					ConfigurationClassParser.this.environment,
					ConfigurationClassParser.this.resourceLoader,
					ConfigurationClassParser.this.registry);
			return group;
		}

	}


	private static class DeferredImportSelectorHolder {

		private final ConfigurationClass configurationClass;

		private final DeferredImportSelector importSelector;

		public DeferredImportSelectorHolder(ConfigurationClass configClass, DeferredImportSelector selector) {
			this.configurationClass = configClass;
			this.importSelector = selector;
		}

		public ConfigurationClass getConfigurationClass() {
			return this.configurationClass;
		}

		public DeferredImportSelector getImportSelector() {
			return this.importSelector;
		}
	}


	private static class DeferredImportSelectorGrouping {

		private final DeferredImportSelector.Group group;

		private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();

		DeferredImportSelectorGrouping(Group group) {
			this.group = group;
		}

		public void add(DeferredImportSelectorHolder deferredImport) {
			this.deferredImports.add(deferredImport);
		}

		/**
		 * Return the imports defined by the group.
		 * @return each import with its associated configuration class
		 */
		public Iterable<Group.Entry> getImports() {
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			return this.group.selectImports();
		}
	}


	private static class DefaultDeferredImportSelectorGroup implements Group {

		private final List<Entry> imports = new ArrayList<>();

		@Override
		public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
			for (String importClassName : selector.selectImports(metadata)) {
				this.imports.add(new Entry(metadata, importClassName));
			}
		}

		@Override
		public Iterable<Entry> selectImports() {
			return this.imports;
		}
	}


	/**
	 * Simple wrapper that allows annotated source classes to be dealt with
	 * in a uniform manner, regardless of how they are loaded.
	 */
	private class SourceClass implements Ordered {

		private final Object source;  // Class or MetadataReader

		private final AnnotationMetadata metadata;

		public SourceClass(Object source) {
			this.source = source;
			if (source instanceof Class) {
				this.metadata = AnnotationMetadata.introspect((Class<?>) source);
			}
			else {
				this.metadata = ((MetadataReader) source).getAnnotationMetadata();
			}
		}

		public final AnnotationMetadata getMetadata() {
			return this.metadata;
		}

		@Override
		public int getOrder() {
			Integer order = ConfigurationClassUtils.getOrder(this.metadata);
			return (order != null ? order : Ordered.LOWEST_PRECEDENCE);
		}

		public Class<?> loadClass() throws ClassNotFoundException {
			if (this.source instanceof Class) {
				return (Class<?>) this.source;
			}
			String className = ((MetadataReader) this.source).getClassMetadata().getClassName();
			return ClassUtils.forName(className, resourceLoader.getClassLoader());
		}

		public boolean isAssignable(Class<?> clazz) throws IOException {
			if (this.source instanceof Class) {
				return clazz.isAssignableFrom((Class<?>) this.source);
			}
			return new AssignableTypeFilter(clazz).match((MetadataReader) this.source, metadataReaderFactory);
		}

		public ConfigurationClass asConfigClass(ConfigurationClass importedBy) {
			if (this.source instanceof Class) {
				return new ConfigurationClass((Class<?>) this.source, importedBy);
			}
			return new ConfigurationClass((MetadataReader) this.source, importedBy);
		}

		public Collection<SourceClass> getMemberClasses() throws IOException {
			Object sourceToProcess = this.source;
			if (sourceToProcess instanceof Class) {
				Class<?> sourceClass = (Class<?>) sourceToProcess;
				try {
					Class<?>[] declaredClasses = sourceClass.getDeclaredClasses();
					List<SourceClass> members = new ArrayList<>(declaredClasses.length);
					for (Class<?> declaredClass : declaredClasses) {
						members.add(asSourceClass(declaredClass));
					}
					return members;
				}
				catch (NoClassDefFoundError err) {
					// getDeclaredClasses() failed because of non-resolvable dependencies
					// -> fall back to ASM below
					sourceToProcess = metadataReaderFactory.getMetadataReader(sourceClass.getName());
				}
			}

			// ASM-based resolution - safe for non-resolvable classes as well
			MetadataReader sourceReader = (MetadataReader) sourceToProcess;
			String[] memberClassNames = sourceReader.getClassMetadata().getMemberClassNames();
			List<SourceClass> members = new ArrayList<>(memberClassNames.length);
			for (String memberClassName : memberClassNames) {
				try {
					members.add(asSourceClass(memberClassName));
				}
				catch (IOException ex) {
					// Let's skip it if it's not resolvable - we're just looking for candidates
					if (logger.isDebugEnabled()) {
						logger.debug("Failed to resolve member class [" + memberClassName +
								"] - not considering it as a configuration class candidate");
					}
				}
			}
			return members;
		}

		public SourceClass getSuperClass() throws IOException {
			if (this.source instanceof Class) {
				return asSourceClass(((Class<?>) this.source).getSuperclass());
			}
			return asSourceClass(((MetadataReader) this.source).getClassMetadata().getSuperClassName());
		}

		public Set<SourceClass> getInterfaces() throws IOException {
			Set<SourceClass> result = new LinkedHashSet<>();
			if (this.source instanceof Class) {
				Class<?> sourceClass = (Class<?>) this.source;
				for (Class<?> ifcClass : sourceClass.getInterfaces()) {
					result.add(asSourceClass(ifcClass));
				}
			}
			else {
				for (String className : this.metadata.getInterfaceNames()) {
					result.add(asSourceClass(className));
				}
			}
			return result;
		}

		public Set<SourceClass> getAnnotations() {
			Set<SourceClass> result = new LinkedHashSet<>();
			if (this.source instanceof Class) {
				Class<?> sourceClass = (Class<?>) this.source;
				for (Annotation ann : sourceClass.getDeclaredAnnotations()) {
					Class<?> annType = ann.annotationType();
					if (!annType.getName().startsWith("java")) {
						try {
							result.add(asSourceClass(annType));
						}
						catch (Throwable ex) {
							// An annotation not present on the classpath is being ignored
							// by the JVM's class loading -> ignore here as well.
						}
					}
				}
			}
			else {
				for (String className : this.metadata.getAnnotationTypes()) {
					if (!className.startsWith("java")) {
						try {
							result.add(getRelated(className));
						}
						catch (Throwable ex) {
							// An annotation not present on the classpath is being ignored
							// by the JVM's class loading -> ignore here as well.
						}
					}
				}
			}
			return result;
		}

		public Collection<SourceClass> getAnnotationAttributes(String annType, String attribute) throws IOException {
			Map<String, Object> annotationAttributes = this.metadata.getAnnotationAttributes(annType, true);
			if (annotationAttributes == null || !annotationAttributes.containsKey(attribute)) {
				return Collections.emptySet();
			}
			String[] classNames = (String[]) annotationAttributes.get(attribute);
			Set<SourceClass> result = new LinkedHashSet<>();
			for (String className : classNames) {
				result.add(getRelated(className));
			}
			return result;
		}

		private SourceClass getRelated(String className) throws IOException {
			if (this.source instanceof Class) {
				try {
					Class<?> clazz = ClassUtils.forName(className, ((Class<?>) this.source).getClassLoader());
					return asSourceClass(clazz);
				}
				catch (ClassNotFoundException ex) {
					// Ignore -> fall back to ASM next, except for core java types.
					if (className.startsWith("java")) {
						throw new NestedIOException("Failed to load class [" + className + "]", ex);
					}
					return new SourceClass(metadataReaderFactory.getMetadataReader(className));
				}
			}
			return asSourceClass(className);
		}

		@Override
		public boolean equals(@Nullable Object other) {
			return (this == other || (other instanceof SourceClass &&
					this.metadata.getClassName().equals(((SourceClass) other).metadata.getClassName())));
		}

		@Override
		public int hashCode() {
			return this.metadata.getClassName().hashCode();
		}

		@Override
		public String toString() {
			return this.metadata.getClassName();
		}
	}


	/**
	 * {@link Problem} registered upon detection of a circular {@link Import}.
	 */
	private static class CircularImportProblem extends Problem {

		public CircularImportProblem(ConfigurationClass attemptedImport, Deque<ConfigurationClass> importStack) {
			super(String.format("A circular @Import has been detected: " +
					"Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " +
					"already present in the current import stack %s", importStack.element().getSimpleName(),
					attemptedImport.getSimpleName(), attemptedImport.getSimpleName(), importStack),
					new Location(importStack.element().getResource(), attemptedImport.getMetadata()));
		}
	}

}
